/*
根据程序输入的命令行参数，获取磁盘总容量、空闲空间容量、簇数量、扇区数量、扇区容量等信息
将空间容量（以字节为单位）动态转换为KB、MB、GB、TB（两种方法）

GetDiskFreeSpace、GetDiskFreeSpaceEx函数
_tprintf函数
tchar.h库
DWORD、DWORD64类型
PULARGE_INTEGER指针类型
*/
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

int ChkArgcNum(int argc, PCTSTR szFuncName);
void GetVolSpace1(PCTSTR szRootPath);
void GetVolSpace2(PCTSTR szDirName);
void ByteToKMGT1(DWORD64 qwVolOfBytes);
void ByteToKMGT2(DWORD64 qwVolOfBytes);
void GetIntPart(DWORD64 qwVolOfBytes, DWORD64 *qwIntPart, int *iCnt);
void GetFloatPart(DWORD64 qwVolOfBytes, DWORD64 qwIntPart, int iCnt, float *fFloatPart);

int main(int argc, PCTSTR argv[])
{
    if (ChkArgcNum(argc, *argv)) {
        // 调用GetDiskFreeSpace函数
        // 获取磁盘容量、簇数量、扇区数量、扇区容量
        GetVolSpace1(*(argv+1));
        // 调用GetDiskFreeSpaceEx函数
        // 获取磁盘容量、空闲空间容量
        GetVolSpace2(*(argv+1));
    }

    return 0;
}

/* 检查程序的命令行参数是否2个 */
int ChkArgcNum(int argc, PCTSTR szFuncName)
{
    if (argc!=2) {
        _tprintf(TEXT("请以如下形式输入参数。比如：\n  %s D：\\"), szFuncName);
        return 0;
    }
    return 1;
}

/* GetDiskFreeSpace获取磁盘容量、簇数量、扇区数量、扇区容量 */
void GetVolSpace1(PCTSTR szRootPath)
{
    // 每簇的扇区数量
    DWORD dwSectorsPerCluster;
    // 扇区的容量
    DWORD dwBytesPerSector;
    // 空闲的簇数量
    DWORD dwNumberOfFreeClusters;
    // 总簇数量
    DWORD dwTotalNumberOfClusters;
    // 磁盘空余空间容量
    DWORD64 qwTotalNumberOfFreeBytes;
    // 磁盘总容量
    DWORD64 qwTotalNumberOfBytes;

    if (GetDiskFreeSpace(szRootPath, &dwSectorsPerCluster, &dwBytesPerSector, &dwNumberOfFreeClusters, &dwTotalNumberOfClusters)) {
        _tprintf(TEXT("\n\n使用 GetDiskFreeSpace 获取磁盘空间信息\n"));

        _tprintf(TEXT("空闲的簇数量： %lu\n"), dwNumberOfFreeClusters);
        _tprintf(TEXT("总簇数量： %lu\n"), dwTotalNumberOfClusters);
        _tprintf(TEXT("每簇的扇区数量： %lu\n"), dwSectorsPerCluster);
        _tprintf(TEXT("每扇区的容量（字节）： %lu\n"), dwBytesPerSector);

        qwTotalNumberOfFreeBytes = (DWORD64)dwNumberOfFreeClusters * dwSectorsPerCluster * dwBytesPerSector;
        qwTotalNumberOfBytes = (DWORD64)dwTotalNumberOfClusters * dwSectorsPerCluster * dwBytesPerSector;
        //  %lld和%llu，gcc/g++的格式化输出符
        // %I64d和%I64u，Microsoft VC++库的格式化输出符
        _tprintf(TEXT("空闲空间（字节）： %I64d Byte"), qwTotalNumberOfFreeBytes);
        ByteToKMGT1(qwTotalNumberOfFreeBytes);
        _tprintf(TEXT("磁盘总容量（字节）： %I64d Byte"), qwTotalNumberOfBytes);
        ByteToKMGT1(qwTotalNumberOfBytes);
    }
}

/* GetDiskFreeSpaceEx获取磁盘容量、空闲空间容量 */
void GetVolSpace2(PCTSTR szDirName)
{
    DWORD64 qwFreeBytesAvailableToCaller;
    DWORD64 qwTotalNumberOfBytes;
    DWORD64 qwTotalNumberOfFreeBytes;

    if (GetDiskFreeSpaceEx(szDirName, (PULARGE_INTEGER)&qwFreeBytesAvailableToCaller, (PULARGE_INTEGER)&qwTotalNumberOfBytes, (PULARGE_INTEGER)&qwTotalNumberOfFreeBytes)) {
        _tprintf(TEXT("\n\n使用 GetDiskFreeSpaceEx 获取磁盘空间信息\n"));

        _tprintf(TEXT("用户可获得的空闲空间（字节）: %I64d Byte"), qwFreeBytesAvailableToCaller);
        ByteToKMGT2(qwFreeBytesAvailableToCaller);
        _tprintf(TEXT("空闲空间（字节）: %I64d Byte"), qwTotalNumberOfFreeBytes);
        ByteToKMGT2(qwTotalNumberOfFreeBytes);
        _tprintf(TEXT("磁盘总容量（字节）： %I64d Byte"), qwTotalNumberOfBytes);
        ByteToKMGT2(qwTotalNumberOfBytes);
    }
}

/* 第1种方法：把字节动态转换为KB、MB、GB、TB */
void ByteToKMGT1(DWORD64 qwVolOfBytes)
{
    DWORD64 qwTemp = qwVolOfBytes;
    // 统计换算1024的次数
    int iCnt = 0;

    while (qwTemp>=1024) {
        qwTemp /= 1024;
        iCnt++;
    }

    switch (iCnt) {
        case 0:
            _tprintf(TEXT("，共计 %d B\n"), (int)qwVolOfBytes);
            break;
        case 1:
            _tprintf(TEXT("，共计 %.3f KB\n"), qwVolOfBytes/(float)1024);
            break;
        case 2:
            _tprintf(TEXT("，共计 %.3f MB\n"), qwVolOfBytes/((float)1024*1024));
            break;
        case 3:
            _tprintf(TEXT("，共计 %.3f GB\n"), qwVolOfBytes/((float)1024*1024*1024));
            break;
        case 4:
            _tprintf(TEXT("，共计 %.3f TB\n"), qwVolOfBytes/((float)1024*1024*1024*1024));
            break;
        default :
            break;            
    }
}

/* 第2种方法：把字节动态转换为KB、MB、GB、TB */
void ByteToKMGT2(DWORD64 qwVolOfBytes)
{
    // 统计换算1024的次数
    int iCnt = 0;
    // 动态转换后的数字的整数部分
    // 不能定义成int型
    // 因为调用GetFloatPart函数时
    // 执行qwIntPart *= 1024运算，可能会超出int型的最大值
    DWORD64 qwIntPart;
    // 动态转换后的数字的浮点数部分
    float fFloatPart = 0.0;

    // 获取动态转换后的数字的整数部分
    GetIntPart(qwVolOfBytes, &qwIntPart, &iCnt);
    // 获取动态转换后的数字的浮点数部分
    GetFloatPart(qwVolOfBytes, qwIntPart, iCnt, &fFloatPart);

    switch (iCnt) {
        case 0:
            _tprintf(TEXT("，共计 %.3f B\n"), qwIntPart+fFloatPart);
            break;
        case 1:
            _tprintf(TEXT("，共计 %.3f KB\n"), qwIntPart+fFloatPart);
            break;
        case 2:
            _tprintf(TEXT("，共计 %.3f MB\n"), qwIntPart+fFloatPart);
            break;
        case 3:
            _tprintf(TEXT("，共计 %.3f GB\n"), qwIntPart+fFloatPart);
            break;
        case 4:
            _tprintf(TEXT("，共计 %.3f TB\n"), qwIntPart+fFloatPart);
            break;
        default :
            break;            
    }
}

/* 获取动态转换后的数字的整数部分 */
void GetIntPart(DWORD64 qwVolOfBytes, DWORD64 *qwIntPart, int *iCnt)
{
    while (qwVolOfBytes>=1024) {
        qwVolOfBytes /= 1024;
        (*iCnt)++;
    }

    *qwIntPart = qwVolOfBytes;
}

/* 获取动态转换后的数字的浮点数部分 */
void GetFloatPart(DWORD64 qwVolOfBytes, DWORD64 qwIntPart, int iCnt, float *fFloatPart)
{
    // 统计换算1024的次数
    DWORD64 qwDivisor = 1;

    for (int i=0; i<iCnt; i++) {
        qwIntPart *= 1024;
        qwDivisor *= 1024;
    }

    if ((qwVolOfBytes-qwIntPart)!=0) {
        *fFloatPart = (float)(qwVolOfBytes-qwIntPart) / qwDivisor;
    }
}